{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "EheA5_j_cEwc"
      },
      "source": [
        "##### Copyright 2020 Google LLC.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "YCriMWd-pRTP"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "OvRwFTkqcp1e"
      },
      "source": [
        "# Forward and Backward mode gradients in TFF\n",
        "\n",
        "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/google/tf-quant-finance/blob/master/tf_quant_finance/examples/jupyter_notebooks/Forward_Backward_Diff.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://github.com/google/tf-quant-finance/blob/master/tf_quant_finance/examples/jupyter_notebooks/Forward_Backward_Diff.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "\u003c/table\u003e"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "uG8UAZXjeUhf"
      },
      "outputs": [],
      "source": [
        "#@title Upgrade to TensorFlow nightly\n",
        "!pip install --upgrade tf-nightly"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "Xa88brUxqSVF"
      },
      "outputs": [],
      "source": [
        "#@title Install TF Quant Finance\n",
        "!pip install tff-nightly"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ku46LPe6Jswv"
      },
      "source": [
        "### This notebook demonstrates the difference between forward and backward gradient computation\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "2nA2FSdTgcEM"
      },
      "outputs": [],
      "source": [
        "#@title Imports { display-mode: \"form\" }\n",
        "\n",
        "import tensorflow as tf\n",
        "import functools\n",
        "import tf_quant_finance as tff"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "BjOFDMzArFYP"
      },
      "source": [
        "Consider a simple vector-function in two variables $x$ and $y$:\n",
        "\n",
        "$\n",
        "\\begin{align}\n",
        "\u0026 f = [f_1, f_2, f_3] \\\\\n",
        "\u0026 where \\\\\n",
        "\\end{align}\n",
        "$\n",
        "\n",
        "$\n",
        "\\begin{align}\n",
        "f_1 \u0026= x^2 \\\\\n",
        "f_2 \u0026= y^2 \\\\\n",
        "f_3 \u0026= x y \\\\\n",
        "\\end{align}\n",
        "$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "LflVSmESrFkC"
      },
      "outputs": [],
      "source": [
        "def func(x):\n",
        "    func = tf.stack([x[0]**2, x[1]**2, x[0] * x[1]])\n",
        "    return func\n",
        "\n",
        "start = tf.constant([1,2], dtype=tf.float64)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3ufHejiFrd6W"
      },
      "source": [
        "# Backward mode\n",
        "\n",
        "For a vector $u = [u_1, u_2, u_3]$, backward gradient computes partial derivatives of the dot product $u \\cdot f(x, y)$\n",
        "\n",
        "$\n",
        "\\begin{align}\n",
        "\\frac {\\partial (u \\cdot f)}{\\partial x} \u0026= u_1 \\frac{\\partial f_1}{\\partial x} + u_2 \\frac{\\partial f_2}{\\partial x} + u_3 \\frac{\\partial f_3}{\\partial x} \\\\ \\\\\n",
        "\u0026= 2 u_1 x + u_3 y\\\\ \\\\\n",
        "\\frac {\\partial (u \\cdot f)}{\\partial y} \u0026= u_1 \\frac{\\partial f_1}{\\partial y} + u_2 \\frac{\\partial f_2}{\\partial y} + u_3 \\frac{\\partial f_3}{\\partial y} \\\\ \\\\\n",
        "\u0026= 2 u_1 y + u_3 x\n",
        "\\end{align}\n",
        "$\n",
        "\n",
        "In Tensorflow, [$u_1$, $u_2$, $u_3$] is by default set to [1, 1, 1].\n",
        "\n",
        "Setting [$x$, $y$] to [1, 2], backward mode returns the gradients summed up by components"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "colab": {
          "height": 35
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 435,
          "status": "ok",
          "timestamp": 1591085681659,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "XJsbnL4YrbKd",
        "outputId": "bb8df56a-32c3-442b-fad2-471d1f82fb71"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(2,), dtype=float64, numpy=array([4., 5.])\u003e"
            ]
          },
          "execution_count": 4,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Note that the output is u  d(u.f(x, y))dx and d(u.f(x, y))dy\n",
        "tff.math.gradients(func, start)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ItgjztYFtYJj"
      },
      "source": [
        "The user has access to [$u_1$, $u_2$, $u_3$] as well. Setting the values\n",
        "to [0, 0, 1] leads to the gradient $[\\frac{\\partial f_3}{\\partial x}, \\frac{\\partial f_3}{\\partial y}]$\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {
          "height": 35
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 444,
          "status": "ok",
          "timestamp": 1591029891915,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "FvnPDIPftU5c",
        "outputId": "d0cedc28-20e1-4c95-b5af-c2f7d4403936"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(2,), dtype=float64, numpy=array([1., 1.])\u003e"
            ]
          },
          "execution_count": 21,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "tff.math.gradients(func, start, \n",
        "                   output_gradients=tf.constant([0, 0, 1], dtype=tf.float64))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "j1JYjcoAr-Mm"
      },
      "source": [
        "# Forward mode\n",
        "\n",
        "TFF provides an opportunity to compute a forward gradient as well.\n",
        "For a vector $w = [w_1, w_2]$, forward gradient computes differentials for $[f_1, f_2, f_3]$\n",
        "\n",
        "$\n",
        "\\begin{align}\n",
        "{\\partial f_1} \u0026= w_1 \\frac{\\partial f_1}{\\partial x} + w_2 \\frac{\\partial f_1}{\\partial y} \\\\ \\\\\n",
        "\u0026= 2 w_1 x \\\\ \\\\\n",
        "{\\partial f_2} \u0026= w_1 \\frac{\\partial f_2}{\\partial x} + w_2 \\frac{\\partial f_2}{\\partial y} \\\\ \\\\\n",
        "\u0026= 2 w_2 y \\\\ \\\\\n",
        "{\\partial f_3} \u0026= w_1 \\frac{\\partial f_3}{\\partial x} + w_2 \\frac{\\partial f_3}{\\partial y} \\\\ \\\\\n",
        "\u0026= w_1 x + w_2 y \\\\ \\\\\n",
        "\\end{align}\n",
        "$\n",
        "\n",
        "In TFF, [$w_1$, $w_2$] is by default set to [1, 1]. Setting [$x$, $y$] to [1, 2], forward mode returns the differentials by components."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "colab": {
          "height": 35
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 450,
          "status": "ok",
          "timestamp": 1591085936168,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "pCF1KvxMrpKC",
        "outputId": "39743836-ac70-4594-95b6-c42dcaa86c27"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(3,), dtype=float64, numpy=array([2., 4., 3.])\u003e"
            ]
          },
          "execution_count": 5,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "tff.math.fwd_gradient(func, start)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "o7qpw6QosHC3"
      },
      "source": [
        "Remember, Tensorflow is the tool commonly used in Machine Learning. In Machine Learning, the aim is to minmize the scalar loss function, the loss function being the sum of the gradient with respect to the feature set. This lowest loss is the loss summed up over all training examples which can be computed via backward gradient.\n",
        "\n",
        "However, let's take the use case where we are valuing a set of options, say ten, against a single spot price $S_0$. We now have ten price functions and we need their gradients against spot $S_0$ (ten deltas).\n",
        "\n",
        "Using the forward gradients with respect to $S_0$ would give us the ten delta's in a single pass.\n",
        "\n",
        "Using the backward gradients would result in the sum of the ten delta's, which may not be that useful.\n",
        "\n",
        "It is useful to note that varying the weights would also give you individual components of the gradients (in other words [1, 0] and [0, 1] as values of [$w_1$, $w_2$], instead of the default [1, 1], similarly for backward. This is, of course, at the expense of more compute."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "colab": {
          "height": 35
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 432,
          "status": "ok",
          "timestamp": 1591086024897,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "dIs_GSZKsD2-",
        "outputId": "1a6681d3-71e4-4317-f21a-a82069cb2878"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(3,), dtype=float64, numpy=array([2., 0., 2.])\u003e"
            ]
          },
          "execution_count": 6,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "tff.math.fwd_gradient(func, start,\n",
        "                      input_gradients=tf.constant([1.0, 0.0], dtype=tf.float64))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "colab": {
          "height": 35
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 48,
          "status": "ok",
          "timestamp": 1591086025455,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "lPDaK656sTuG",
        "outputId": "e36203e1-9bdf-48c3-bd41-889035a812e1"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(3,), dtype=float64, numpy=array([0. , 0.4, 0.1])\u003e"
            ]
          },
          "execution_count": 7,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "tff.math.fwd_gradient(func, start,\n",
        "                      input_gradients=tf.constant([0.0, 0.1], dtype=tf.float64))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "kT7EZrNQsoHY"
      },
      "outputs": [],
      "source": [
        ""
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "last_runtime": {
        "build_target": "",
        "kind": "local"
      },
      "name": "Forward_Backward_Diff.ipynb",
      "provenance": [
        {
          "file_id": "1rT50234_4wb5VFNWyheBfyVlV-iuRA0t",
          "timestamp": 1553699488325
        },
        {
          "file_id": "1G2Wm6NzpdH3gPFsFTaLslgonTTkI0U0h",
          "timestamp": 1552493606270
        }
      ],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
